package com.fastloan.core.apiauth.base.service.impl.ry;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fastloan.core.apiauth.base.service.GeneralEncrypt;
import com.fastloan.core.apiauth.base.util.AesUtil;
import com.fastloan.core.apiauth.base.util.EncryptTool;
import com.fastloan.core.apiauth.base.util.RsaUtil;

import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 融钰加密模式
 *
 * @author yanwe
 * createTime 2018-01-2018/1/4 13:07
 */
public class RYEncryptMode extends GeneralEncrypt {

    private String encryptAesKey;

    public String getEncryptAesKey() {
        return encryptAesKey;
    }

    public void setEncryptAesKey(String encryptAesKey) {
        this.encryptAesKey = encryptAesKey;
    }

    @Override
    public String signBean(Object object,String key) {
        try {
            setKey(key);
            Map<String, Object> beanMap;
            if (object instanceof JSONObject){
                beanMap = JSON.parseObject(JSON.toJSONString(object, SerializerFeature.WriteMapNullValue)
                        ,new TypeReference<Map<String,Object>>(){});
            } else {
                beanMap = getBeanMap(object);
            }
            String splice = sortInfoAndConcat(beanMap);
            String signature = generateSign(getKey(),splice);
            logger.debug("签名结果:"+signature);
            return signature;
        }catch (Exception ex){
            ex.printStackTrace();
            logger.error(ex.toString());
            return null;
        }
    }

    /**
     * 生成签名
     *
     * @param key key
     * @param data 数据
     * @return 签名
     */
    public String generateSign(String key,String data) {
        try{
            return EncryptTool.HMACSHA256Encode(key, data);
        }catch (Exception ex){
            ex.printStackTrace();
            logger.error(ex.toString());
            return "";
        }
    }

    @Override
    public String encryptBean(Object object,String key) {
        setKey(key);
        return encryptBeanBase(object,RsaUtil::encryptByRsaPub,this::getRsaPubKey);
    }

    @Override
    public String decodeBean(String data) {
        return decodeBeanBase(data,RsaUtil::decryptByRsaPri,this::getRsaPriKey);
    }

    /**
     * 私钥加密
     *
     * @param object obj
     * @return 私钥加密结果
     */
    public String encryptBeanByRsaPri(Object object,String key){
        setKey(key);
        return encryptBeanBase(object,RsaUtil::encryptByRsaPri,this::getRsaPriKey);
    }

    /**
     * 公钥解密
     *
     * @param data data
     * @return 解密结果
     */
    public String decodeBeanPub(String data) {
        return decodeBeanBase(data,RsaUtil::decryptByRsaPub,this::getRsaPubKey);
    }

    private String encryptBeanBase(Object object, BiFunction<String,String,String> function,Supplier<String> supplier) {
        try{
            String secret = EncryptTool.generateRandom32();
            String iv = EncryptTool.generateRandom16();
            setAesIv(iv);
            setAesKey(secret);
            Map<String, Object> beanMap = getBeanMap(object);
            String sign = signBean(object,getKey());
            beanMap.put("sign",sign);
            String encryptData = AesUtil.encryptByCBC(JSON.toJSONString(beanMap, SerializerFeature.WriteMapNullValue), secret, iv);
            String sc = function.apply(secret,supplier.get());
            setEncryptAesKey(sc);
            logger.debug("加密结果:" + encryptData);
            return encryptData;
        }catch (Exception ex){
            ex.printStackTrace();
            logger.error("加密失败:"+ex.getMessage());
            return null;
        }
    }

    private String decodeBeanBase(String data,BiFunction<String,String,String> function,Supplier<String> supplier){
        logger.debug("解密信息:" + data);
        String sc = function.apply(getEncryptAesKey(),supplier.get());
        setAesKey(sc);
        logger.debug("密钥:"+sc);
        if (sc == null){
            logger.warn("sc解密失败:" + data);
            return null;
        }
        String decryptData = AesUtil.decryptByCBC(data, sc, getAesIv());
        if (decryptData == null){
            logger.warn("data解密失败:" + data);
            return null;
        }
        logger.debug("解密数据:" + decryptData);
        return decryptData;
    }

    /**
     * 排序并拼接为字符串
     *
     * @param map 请求map
     * @return 拼接数据
     */
    private String sortInfoAndConcat(Map<String, Object> map){
        return map.entrySet().stream().sorted(Map.Entry.comparingByKey())
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));
    }
}
